{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "bd1e29cf-bfb5-405c-bb4c-8ffff937be93",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025/03/04 20:50:09] ppocr DEBUG: Namespace(help='==SUPPRESS==', use_gpu=False, use_xpu=False, use_npu=False, use_mlu=False, ir_optim=True, use_tensorrt=False, min_subgraph_size=15, precision='fp32', gpu_mem=500, gpu_id=0, image_dir=None, page_num=0, det_algorithm='DB', det_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\det\\\\ch\\\\ch_PP-OCRv4_det_infer', det_limit_side_len=960, det_limit_type='max', det_box_type='quad', det_db_thresh=0.1, det_db_box_thresh=0.1, det_db_unclip_ratio=2, max_batch_size=10, use_dilation=False, det_db_score_mode='fast', det_east_score_thresh=0.8, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_sast_score_thresh=0.5, det_sast_nms_thresh=0.2, det_pse_thresh=0, det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, scales=[8, 16, 32], alpha=1.0, beta=1.0, fourier_degree=5, rec_algorithm='SVTR_LCNet', rec_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\rec\\\\ch\\\\ch_PP-OCRv4_rec_infer', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_batch_num=6, max_text_length=25, rec_char_dict_path='D:\\\\power\\\\anaconda3\\\\envs\\\\ai\\\\Lib\\\\site-packages\\\\paddleocr\\\\ppocr\\\\utils\\\\ppocr_keys_v1.txt', use_space_char=True, vis_font_path='./doc/fonts/simfang.ttf', drop_score=0.5, e2e_algorithm='PGNet', e2e_model_dir=None, e2e_limit_side_len=768, e2e_limit_type='max', e2e_pgnet_score_thresh=0.5, e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_pgnet_valid_set='totaltext', e2e_pgnet_mode='fast', use_angle_cls=True, cls_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\cls\\\\ch_ppocr_mobile_v2.0_cls_infer', cls_image_shape='3, 48, 192', label_list=['0', '180'], cls_batch_num=6, cls_thresh=0.9, enable_mkldnn=False, cpu_threads=10, use_pdserving=False, warmup=False, sr_model_dir=None, sr_image_shape='3, 32, 128', sr_batch_num=1, draw_img_save_dir='./inference_results', save_crop_res=False, crop_res_save_dir='./output', use_mp=False, total_process_num=1, process_id=0, benchmark=False, save_log_path='./log_output/', show_log=True, use_onnx=False, return_word_box=False, output='./output', table_max_len=488, table_algorithm='TableAttn', table_model_dir=None, merge_no_span_structure=True, table_char_dict_path=None, formula_algorithm='LaTeXOCR', formula_model_dir=None, formula_char_dict_path=None, formula_batch_num=1, layout_model_dir=None, layout_dict_path=None, layout_score_threshold=0.5, layout_nms_threshold=0.5, kie_algorithm='LayoutXLM', ser_model_dir=None, re_model_dir=None, use_visual_backbone=True, ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ocr_order_method=None, mode='structure', image_orientation=False, layout=True, table=True, formula=False, ocr=True, recovery=False, recovery_to_markdown=False, use_pdf2docx_api=False, invert=False, binarize=False, alphacolor=(255, 255, 255), lang='ch', det=True, rec=True, type='ocr', savefile=False, ocr_version='PP-OCRv4', structure_version='PP-StructureV2', det_db_box_size=3)\n",
      "[2025/03/04 20:50:10] ppocr DEBUG: dt_boxes num : 23, elapsed : 0.10659480094909668\n",
      "[2025/03/04 20:50:10] ppocr DEBUG: cls num  : 23, elapsed : 0.04151129722595215\n",
      "[2025/03/04 20:50:11] ppocr DEBUG: rec_res num  : 23, elapsed : 0.2117455005645752\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from paddleocr import PaddleOCR\n",
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "# 初始化OCR模型，调整参数以检测单个字符\n",
    "ocr = PaddleOCR(\n",
    "    use_angle_cls=True,          # 启用方向分类\n",
    "    lang='ch',                   # 中文模型\n",
    "    det_db_thresh=0.1,           # 降低二值化阈值\n",
    "    det_db_box_thresh=0.1,       # 降低框得分阈值\n",
    "    det_db_unclip_ratio=2,     # 扩大框扩展比例\n",
    "    det_db_box_size=3            # 设置最小框尺寸\n",
    ")\n",
    "\n",
    "# 读取图片\n",
    "image_path = './img/kongzi/new_image00624.jpeg'\n",
    "image = cv2.imread(image_path)\n",
    "\n",
    "# 执行OCR检测\n",
    "result = ocr.ocr(image_path, cls=True)\n",
    "\n",
    "# 遍历所有检测到的文本行\n",
    "for line in result:\n",
    "    for word_info in line:\n",
    "        box = word_info[0]  # 提取坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "        text = word_info[1][0]  # 提取识别文本\n",
    "        \n",
    "        # 将坐标转换为 OpenCV 所需的整数格式\n",
    "        box = np.array(box, dtype=np.int32)\n",
    "        \n",
    "        # 绘制文本行的矩形框（绿色）\n",
    "        # cv2.polylines(image, [box], isClosed=True, color=(0, 255, 0), thickness=2)\n",
    "        \n",
    "        # 计算每个字符的宽度\n",
    "        char_width = (box[1][0] - box[0][0]) / len(text)  # 假设字符宽度相等\n",
    "        \n",
    "        # 遍历每个字符并绘制独立框\n",
    "        for i, char in enumerate(text):\n",
    "            # 计算当前字符的四个顶点坐标\n",
    "            char_box = np.array([\n",
    "                [box[0][0] + i * char_width, box[0][1]],  # 左上角\n",
    "                [box[0][0] + (i + 1) * char_width, box[1][1]],  # 右上角\n",
    "                [box[3][0] + (i + 1) * char_width, box[2][1]],  # 右下角\n",
    "                [box[3][0] + i * char_width, box[3][1]],  # 左下角\n",
    "            ], dtype=np.int32)\n",
    "            \n",
    "            # 绘制字符的矩形框（红色）\n",
    "            cv2.polylines(image, [char_box], isClosed=True, color=(0, 0, 255), thickness=2)\n",
    "\n",
    "# 保存结果\n",
    "cv2.imwrite('./img/kongzi/output_paddleocr_chars_split.jpg', image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "69339bb8-1508-471e-b498-025d5eb488b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025/03/04 20:55:57] ppocr DEBUG: Namespace(help='==SUPPRESS==', use_gpu=False, use_xpu=False, use_npu=False, use_mlu=False, ir_optim=True, use_tensorrt=False, min_subgraph_size=15, precision='fp32', gpu_mem=500, gpu_id=0, image_dir=None, page_num=0, det_algorithm='DB', det_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\det\\\\ch\\\\ch_PP-OCRv4_det_infer', det_limit_side_len=960, det_limit_type='max', det_box_type='quad', det_db_thresh=0.1, det_db_box_thresh=0.1, det_db_unclip_ratio=2, max_batch_size=10, use_dilation=False, det_db_score_mode='fast', det_east_score_thresh=0.8, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_sast_score_thresh=0.5, det_sast_nms_thresh=0.2, det_pse_thresh=0, det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, scales=[8, 16, 32], alpha=1.0, beta=1.0, fourier_degree=5, rec_algorithm='SVTR_LCNet', rec_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\rec\\\\ch\\\\ch_PP-OCRv4_rec_infer', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_batch_num=6, max_text_length=25, rec_char_dict_path='D:\\\\power\\\\anaconda3\\\\envs\\\\ai\\\\Lib\\\\site-packages\\\\paddleocr\\\\ppocr\\\\utils\\\\ppocr_keys_v1.txt', use_space_char=True, vis_font_path='./doc/fonts/simfang.ttf', drop_score=0.5, e2e_algorithm='PGNet', e2e_model_dir=None, e2e_limit_side_len=768, e2e_limit_type='max', e2e_pgnet_score_thresh=0.5, e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_pgnet_valid_set='totaltext', e2e_pgnet_mode='fast', use_angle_cls=True, cls_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\cls\\\\ch_ppocr_mobile_v2.0_cls_infer', cls_image_shape='3, 48, 192', label_list=['0', '180'], cls_batch_num=6, cls_thresh=0.9, enable_mkldnn=False, cpu_threads=10, use_pdserving=False, warmup=False, sr_model_dir=None, sr_image_shape='3, 32, 128', sr_batch_num=1, draw_img_save_dir='./inference_results', save_crop_res=False, crop_res_save_dir='./output', use_mp=False, total_process_num=1, process_id=0, benchmark=False, save_log_path='./log_output/', show_log=True, use_onnx=False, return_word_box=False, output='./output', table_max_len=488, table_algorithm='TableAttn', table_model_dir=None, merge_no_span_structure=True, table_char_dict_path=None, formula_algorithm='LaTeXOCR', formula_model_dir=None, formula_char_dict_path=None, formula_batch_num=1, layout_model_dir=None, layout_dict_path=None, layout_score_threshold=0.5, layout_nms_threshold=0.5, kie_algorithm='LayoutXLM', ser_model_dir=None, re_model_dir=None, use_visual_backbone=True, ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ocr_order_method=None, mode='structure', image_orientation=False, layout=True, table=True, formula=False, ocr=True, recovery=False, recovery_to_markdown=False, use_pdf2docx_api=False, invert=False, binarize=False, alphacolor=(255, 255, 255), lang='ch', det=True, rec=True, type='ocr', savefile=False, ocr_version='PP-OCRv4', structure_version='PP-StructureV2', det_db_box_size=5)\n",
      "[2025/03/04 20:55:58] ppocr DEBUG: dt_boxes num : 23, elapsed : 0.10805630683898926\n",
      "[2025/03/04 20:55:58] ppocr DEBUG: cls num  : 23, elapsed : 0.038443803787231445\n",
      "[2025/03/04 20:55:58] ppocr DEBUG: rec_res num  : 23, elapsed : 0.20179510116577148\n",
      "结果已保存到: output_paddleocr_rectangles.jpg\n"
     ]
    }
   ],
   "source": [
    "from paddleocr import PaddleOCR\n",
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "def draw_rectangle(image, box, color=(0, 255, 0), thickness=2):\n",
    "    \"\"\"\n",
    "    绘制轴对齐的矩形框\n",
    "    :param image: 原始图片\n",
    "    :param box: 四边形的四个顶点坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "    :param color: 框的颜色\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    # 将四边形转换为最小外接矩形\n",
    "    rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "    x, y, w, h = rect\n",
    "\n",
    "    # 绘制矩形框\n",
    "    cv2.rectangle(image, (x, y), (x + w, y + h), color, thickness)\n",
    "\n",
    "def draw_char_rectangles(image, rect, text, color=(0, 0, 255), thickness=2):\n",
    "    \"\"\"\n",
    "    绘制字符的矩形框\n",
    "    :param image: 原始图片\n",
    "    :param rect: 文本行的矩形框 (x, y, w, h)\n",
    "    :param text: 识别文本\n",
    "    :param color: 框的颜色\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    x, y, w, h = rect\n",
    "    char_width = w / len(text)  # 假设字符宽度相等\n",
    "\n",
    "    for i, char in enumerate(text):\n",
    "        # 计算当前字符的矩形框\n",
    "        char_x = int(x + i * char_width)\n",
    "        char_y = y\n",
    "        char_w = int(char_width)\n",
    "        char_h = h\n",
    "\n",
    "        # 绘制字符的矩形框\n",
    "        cv2.rectangle(image, (char_x, char_y), (char_x + char_w, char_y + char_h), color, thickness)\n",
    "\n",
    "def main():\n",
    "    # 初始化OCR模型\n",
    "    ocr = PaddleOCR(\n",
    "        use_angle_cls=True,          # 启用方向分类\n",
    "        lang='ch',                   # 中文模型\n",
    "        det_db_thresh=0.1,           # 降低二值化阈值\n",
    "        det_db_box_thresh=0.1,       # 降低框得分阈值\n",
    "        det_db_unclip_ratio=2,     # 扩大框扩展比例\n",
    "        det_db_box_size=5            # 设置最小框尺寸\n",
    "    )\n",
    "\n",
    "    # 读取图片\n",
    "    image_path = './img/kongzi/new_image00624.jpeg'\n",
    "    try:\n",
    "        image = cv2.imread(image_path)\n",
    "        if image is None:\n",
    "            raise FileNotFoundError(f\"图片 {image_path} 未找到或无法读取\")\n",
    "    except Exception as e:\n",
    "        print(f\"图片读取失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 执行OCR检测\n",
    "    try:\n",
    "        result = ocr.ocr(image_path, cls=True)\n",
    "    except Exception as e:\n",
    "        print(f\"OCR检测失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 遍历所有检测到的文本行\n",
    "    for line in result:\n",
    "        for word_info in line:\n",
    "            box = word_info[0]  # 提取坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "            text = word_info[1][0]  # 提取识别文本\n",
    "\n",
    "            # 绘制文本行的矩形框（绿色）\n",
    "            draw_rectangle(image, box, color=(0, 255, 0), thickness=2)\n",
    "\n",
    "            # 将四边形转换为最小外接矩形\n",
    "            rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "\n",
    "            # 绘制字符的矩形框（红色）\n",
    "            draw_char_rectangles(image, rect, text, color=(0, 0, 255), thickness=2)\n",
    "\n",
    "    # 保存结果\n",
    "    output_path = 'output_paddleocr_rectangles.jpg'\n",
    "    cv2.imwrite(output_path, image)\n",
    "    print(f\"结果已保存到: {output_path}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "41b30477-0744-4640-9bd6-70fe5c1d3383",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025/03/04 21:17:29] ppocr DEBUG: Namespace(help='==SUPPRESS==', use_gpu=False, use_xpu=False, use_npu=False, use_mlu=False, ir_optim=True, use_tensorrt=False, min_subgraph_size=15, precision='fp32', gpu_mem=500, gpu_id=0, image_dir=None, page_num=0, det_algorithm='DB', det_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\det\\\\ch\\\\ch_PP-OCRv4_det_infer', det_limit_side_len=960, det_limit_type='max', det_box_type='quad', det_db_thresh=0.1, det_db_box_thresh=0.1, det_db_unclip_ratio=2, max_batch_size=10, use_dilation=False, det_db_score_mode='fast', det_east_score_thresh=0.8, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_sast_score_thresh=0.5, det_sast_nms_thresh=0.2, det_pse_thresh=0, det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, scales=[8, 16, 32], alpha=1.0, beta=1.0, fourier_degree=5, rec_algorithm='SVTR_LCNet', rec_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\rec\\\\ch\\\\ch_PP-OCRv4_rec_infer', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_batch_num=6, max_text_length=25, rec_char_dict_path='D:\\\\power\\\\anaconda3\\\\envs\\\\ai\\\\Lib\\\\site-packages\\\\paddleocr\\\\ppocr\\\\utils\\\\ppocr_keys_v1.txt', use_space_char=True, vis_font_path='./doc/fonts/simfang.ttf', drop_score=0.5, e2e_algorithm='PGNet', e2e_model_dir=None, e2e_limit_side_len=768, e2e_limit_type='max', e2e_pgnet_score_thresh=0.5, e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_pgnet_valid_set='totaltext', e2e_pgnet_mode='fast', use_angle_cls=True, cls_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\cls\\\\ch_ppocr_mobile_v2.0_cls_infer', cls_image_shape='3, 48, 192', label_list=['0', '180'], cls_batch_num=6, cls_thresh=0.9, enable_mkldnn=False, cpu_threads=10, use_pdserving=False, warmup=False, sr_model_dir=None, sr_image_shape='3, 32, 128', sr_batch_num=1, draw_img_save_dir='./inference_results', save_crop_res=False, crop_res_save_dir='./output', use_mp=False, total_process_num=1, process_id=0, benchmark=False, save_log_path='./log_output/', show_log=True, use_onnx=False, return_word_box=False, output='./output', table_max_len=488, table_algorithm='TableAttn', table_model_dir=None, merge_no_span_structure=True, table_char_dict_path=None, formula_algorithm='LaTeXOCR', formula_model_dir=None, formula_char_dict_path=None, formula_batch_num=1, layout_model_dir=None, layout_dict_path=None, layout_score_threshold=0.5, layout_nms_threshold=0.5, kie_algorithm='LayoutXLM', ser_model_dir=None, re_model_dir=None, use_visual_backbone=True, ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ocr_order_method=None, mode='structure', image_orientation=False, layout=True, table=True, formula=False, ocr=True, recovery=False, recovery_to_markdown=False, use_pdf2docx_api=False, invert=False, binarize=False, alphacolor=(255, 255, 255), lang='ch', det=True, rec=True, type='ocr', savefile=False, ocr_version='PP-OCRv4', structure_version='PP-StructureV2', det_db_box_size=5)\n",
      "[2025/03/04 21:17:30] ppocr DEBUG: dt_boxes num : 17, elapsed : 0.10187053680419922\n",
      "[2025/03/04 21:17:30] ppocr DEBUG: cls num  : 17, elapsed : 0.03368806838989258\n",
      "[2025/03/04 21:17:31] ppocr DEBUG: rec_res num  : 17, elapsed : 0.15070056915283203\n",
      "结果已保存到: ./img/kongzi/output_paddleocr_colorful_rectangles.jpg\n"
     ]
    }
   ],
   "source": [
    "from paddleocr import PaddleOCR\n",
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "def draw_rectangle(image, box, color=(0, 255, 0), thickness=2):\n",
    "    \"\"\"\n",
    "    绘制轴对齐的矩形框\n",
    "    :param image: 原始图片\n",
    "    :param box: 四边形的四个顶点坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "    :param color: 框的颜色\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    # 将四边形转换为最小外接矩形\n",
    "    rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "    x, y, w, h = rect\n",
    "\n",
    "    # 绘制矩形框\n",
    "    cv2.rectangle(image, (x, y), (x + w, y + h), color, thickness)\n",
    "\n",
    "def draw_char_rectangles(image, rect, text, thickness=2):\n",
    "    \"\"\"\n",
    "    绘制字符的矩形框，并交替使用红、黄、蓝三色\n",
    "    :param image: 原始图片\n",
    "    :param rect: 文本行的矩形框 (x, y, w, h)\n",
    "    :param text: 识别文本\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    x, y, w, h = rect\n",
    "    char_width = w / len(text)  # 假设字符宽度相等\n",
    "\n",
    "    # 定义红、黄、蓝三色\n",
    "    colors = [\n",
    "        (0, 0, 255),  # 红色\n",
    "        (0, 255, 255),  # 黄色\n",
    "        (243, 118, 38)  # 橙色\n",
    "    ]\n",
    "\n",
    "    for i, char in enumerate(text):\n",
    "        # 计算当前字符的矩形框\n",
    "        char_x = int(x + i * char_width)\n",
    "        char_y = y\n",
    "        char_w = int(char_width)\n",
    "        char_h = h\n",
    "\n",
    "        # 选择颜色（红、黄、蓝交替）\n",
    "        color = colors[i % 3]\n",
    "\n",
    "        # 绘制字符的矩形框\n",
    "        cv2.rectangle(image, (char_x, char_y), (char_x + char_w, char_y + char_h), color, thickness)\n",
    "\n",
    "def main():\n",
    "    # 初始化OCR模型\n",
    "    ocr = PaddleOCR(\n",
    "        use_angle_cls=True,          # 启用方向分类\n",
    "        lang='ch',                   # 中文模型\n",
    "        det_db_thresh=0.1,           # 降低二值化阈值\n",
    "        det_db_box_thresh=0.1,       # 降低框得分阈值\n",
    "        det_db_unclip_ratio=2,     # 扩大框扩展比例\n",
    "        det_db_box_size=5            # 设置最小框尺寸\n",
    "    )\n",
    "\n",
    "    # 读取图片\n",
    "    image_path = './img/kongzi/new_image00869.jpeg'\n",
    "    try:\n",
    "        image = cv2.imread(image_path)\n",
    "        if image is None:\n",
    "            raise FileNotFoundError(f\"图片 {image_path} 未找到或无法读取\")\n",
    "    except Exception as e:\n",
    "        print(f\"图片读取失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 执行OCR检测\n",
    "    try:\n",
    "        result = ocr.ocr(image_path, cls=True)\n",
    "    except Exception as e:\n",
    "        print(f\"OCR检测失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 遍历所有检测到的文本行\n",
    "    for line in result:\n",
    "        for word_info in line:\n",
    "            box = word_info[0]  # 提取坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "            text = word_info[1][0]  # 提取识别文本\n",
    "\n",
    "            # 绘制文本行的矩形框（绿色）\n",
    "            # draw_rectangle(image, box, color=(0, 255, 0), thickness=2)\n",
    "\n",
    "            # 将四边形转换为最小外接矩形\n",
    "            rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "\n",
    "            # 绘制字符的矩形框（红、黄、蓝交替）\n",
    "            draw_char_rectangles(image, rect, text, thickness=1)\n",
    "\n",
    "    # 保存结果\n",
    "    output_path = './img/kongzi/output_paddleocr_colorful_rectangles.jpg'\n",
    "    cv2.imwrite(output_path, image)\n",
    "    print(f\"结果已保存到: {output_path}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "bd069b69-5b78-4849-a4e2-ab2845b5d1d5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025/03/06 20:58:09] ppocr DEBUG: Namespace(help='==SUPPRESS==', use_gpu=False, use_xpu=False, use_npu=False, use_mlu=False, ir_optim=True, use_tensorrt=False, min_subgraph_size=15, precision='fp32', gpu_mem=500, gpu_id=0, image_dir=None, page_num=0, det_algorithm='DB', det_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\det\\\\ch\\\\ch_PP-OCRv4_det_infer', det_limit_side_len=960, det_limit_type='max', det_box_type='quad', det_db_thresh=0.1, det_db_box_thresh=0.1, det_db_unclip_ratio=1.5, max_batch_size=10, use_dilation=False, det_db_score_mode='fast', det_east_score_thresh=0.8, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_sast_score_thresh=0.5, det_sast_nms_thresh=0.2, det_pse_thresh=0, det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, scales=[8, 16, 32], alpha=1.0, beta=1.0, fourier_degree=5, rec_algorithm='SVTR_LCNet', rec_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\rec\\\\ch\\\\ch_PP-OCRv4_rec_infer', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_batch_num=6, max_text_length=25, rec_char_dict_path='D:\\\\power\\\\anaconda3\\\\envs\\\\ai\\\\Lib\\\\site-packages\\\\paddleocr\\\\ppocr\\\\utils\\\\ppocr_keys_v1.txt', use_space_char=True, vis_font_path='./doc/fonts/simfang.ttf', drop_score=0.5, e2e_algorithm='PGNet', e2e_model_dir=None, e2e_limit_side_len=768, e2e_limit_type='max', e2e_pgnet_score_thresh=0.5, e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_pgnet_valid_set='totaltext', e2e_pgnet_mode='fast', use_angle_cls=True, cls_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\cls\\\\ch_ppocr_mobile_v2.0_cls_infer', cls_image_shape='3, 48, 192', label_list=['0', '180'], cls_batch_num=6, cls_thresh=0.9, enable_mkldnn=False, cpu_threads=10, use_pdserving=False, warmup=False, sr_model_dir=None, sr_image_shape='3, 32, 128', sr_batch_num=1, draw_img_save_dir='./inference_results', save_crop_res=False, crop_res_save_dir='./output', use_mp=False, total_process_num=1, process_id=0, benchmark=False, save_log_path='./log_output/', show_log=True, use_onnx=False, return_word_box=False, output='./output', table_max_len=488, table_algorithm='TableAttn', table_model_dir=None, merge_no_span_structure=True, table_char_dict_path=None, formula_algorithm='LaTeXOCR', formula_model_dir=None, formula_char_dict_path=None, formula_batch_num=1, layout_model_dir=None, layout_dict_path=None, layout_score_threshold=0.5, layout_nms_threshold=0.5, kie_algorithm='LayoutXLM', ser_model_dir=None, re_model_dir=None, use_visual_backbone=True, ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ocr_order_method=None, mode='structure', image_orientation=False, layout=True, table=True, formula=False, ocr=True, recovery=False, recovery_to_markdown=False, use_pdf2docx_api=False, invert=False, binarize=False, alphacolor=(255, 255, 255), lang='ch', det=True, rec=True, type='ocr', savefile=False, ocr_version='PP-OCRv4', structure_version='PP-StructureV2', det_db_box_size=3)\n",
      "[2025/03/06 20:58:11] ppocr DEBUG: dt_boxes num : 10, elapsed : 0.10260939598083496\n",
      "[2025/03/06 20:58:11] ppocr DEBUG: cls num  : 10, elapsed : 0.025007247924804688\n",
      "[2025/03/06 20:58:11] ppocr DEBUG: rec_res num  : 10, elapsed : 0.10968613624572754\n",
      "结果已保存到: ./img/kongzi/output_paddleocr_global_numbered_rectangles.jpg\n"
     ]
    }
   ],
   "source": [
    "from paddleocr import PaddleOCR\n",
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "def draw_rectangle(image, box, color=(0, 255, 0), thickness=2):\n",
    "    \"\"\"\n",
    "    绘制轴对齐的矩形框\n",
    "    :param image: 原始图片\n",
    "    :param box: 四边形的四个顶点坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "    :param color: 框的颜色\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    # 将四边形转换为最小外接矩形\n",
    "    rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "    x, y, w, h = rect\n",
    "\n",
    "    # 绘制矩形框\n",
    "    cv2.rectangle(image, (x, y), (x + w, y + h), color, thickness)\n",
    "\n",
    "def draw_char_rectangles(image, rect, text, counter, thickness=2):\n",
    "    \"\"\"\n",
    "    绘制字符的矩形框，并交替使用红、黄、蓝三色，同时标注全局序号\n",
    "    :param image: 原始图片\n",
    "    :param rect: 文本行的矩形框 (x, y, w, h)\n",
    "    :param text: 识别文本\n",
    "    :param counter: 全局序号计数器\n",
    "    :param thickness: 框的厚度\n",
    "    :return: 更新后的全局序号计数器\n",
    "    \"\"\"\n",
    "    x, y, w, h = rect\n",
    "    char_width = w / len(text)  # 假设字符宽度相等\n",
    "\n",
    "    # 定义红、黄、蓝三色\n",
    "    colors = [\n",
    "        (0, 0, 255),  # 红色\n",
    "        (0, 255, 255),  # 黄色\n",
    "        (255, 0, 0)  # 蓝色\n",
    "    ]\n",
    "\n",
    "    for i, char in enumerate(text):\n",
    "        # 计算当前字符的矩形框\n",
    "        char_x = int(x + i * char_width)\n",
    "        char_y = y\n",
    "        char_w = int(char_width)\n",
    "        char_h = h\n",
    "\n",
    "        # 选择颜色（红、黄、蓝交替）\n",
    "        color = colors[counter % 3]\n",
    "\n",
    "        # 绘制字符的矩形框\n",
    "        cv2.rectangle(image, (char_x, char_y), (char_x + char_w, char_y + char_h), color, thickness)\n",
    "\n",
    "        # 在矩形框内部的左上角标注全局序号\n",
    "        text_position = (char_x + 5, char_y + 20)  # 左上角偏移量\n",
    "        cv2.putText(image, str(counter), text_position, cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, thickness)\n",
    "\n",
    "        # 更新全局序号计数器\n",
    "        counter += 1\n",
    "\n",
    "    return counter\n",
    "\n",
    "def main():\n",
    "    # 初始化OCR模型\n",
    "    ocr = PaddleOCR(\n",
    "        use_angle_cls=True,          # 启用方向分类\n",
    "        lang='ch',                   # 中文模型\n",
    "        det_db_thresh=0.1,           # 降低二值化阈值\n",
    "        det_db_box_thresh=0.1,       # 降低框得分阈值\n",
    "        det_db_unclip_ratio=1.5,     # 扩大框扩展比例\n",
    "        det_db_box_size=3            # 设置最小框尺寸\n",
    "    )\n",
    "\n",
    "    # 读取图片\n",
    "    image_path = './img/kongzi/new_image00873.jpeg'\n",
    "    try:\n",
    "        image = cv2.imread(image_path)\n",
    "        if image is None:\n",
    "            raise FileNotFoundError(f\"图片 {image_path} 未找到或无法读取\")\n",
    "    except Exception as e:\n",
    "        print(f\"图片读取失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 执行OCR检测\n",
    "    try:\n",
    "        result = ocr.ocr(image_path, cls=True)\n",
    "    except Exception as e:\n",
    "        print(f\"OCR检测失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 初始化全局序号计数器\n",
    "    counter = 0\n",
    "\n",
    "    # 遍历所有检测到的文本行\n",
    "    for line in result:\n",
    "        for word_info in line:\n",
    "            box = word_info[0]  # 提取坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "            text = word_info[1][0]  # 提取识别文本\n",
    "\n",
    "            # 绘制文本行的矩形框（绿色）\n",
    "            draw_rectangle(image, box, color=(0, 255, 0), thickness=2)\n",
    "\n",
    "            # 将四边形转换为最小外接矩形\n",
    "            rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "\n",
    "            # 绘制字符的矩形框（红、黄、蓝交替）并标注全局序号\n",
    "            counter = draw_char_rectangles(image, rect, text, counter, thickness=2)\n",
    "\n",
    "    # 保存结果\n",
    "    output_path = './img/kongzi/output_paddleocr_global_numbered_rectangles.jpg'\n",
    "    cv2.imwrite(output_path, image)\n",
    "    print(f\"结果已保存到: {output_path}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "43b19e41-31ca-4e6d-83fd-ef237b5d2667",
   "metadata": {
    "jupyter": {
     "is_executing": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2025/03/12 20:30:38] ppocr DEBUG: Namespace(help='==SUPPRESS==', use_gpu=False, use_xpu=False, use_npu=False, use_mlu=False, ir_optim=True, use_tensorrt=False, min_subgraph_size=15, precision='fp32', gpu_mem=500, gpu_id=0, image_dir=None, page_num=0, det_algorithm='DB', det_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\det\\\\ch\\\\ch_PP-OCRv4_det_infer', det_limit_side_len=960, det_limit_type='max', det_box_type='quad', det_db_thresh=0.1, det_db_box_thresh=0.1, det_db_unclip_ratio=1.5, max_batch_size=10, use_dilation=False, det_db_score_mode='fast', det_east_score_thresh=0.8, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_sast_score_thresh=0.5, det_sast_nms_thresh=0.2, det_pse_thresh=0, det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, scales=[8, 16, 32], alpha=1.0, beta=1.0, fourier_degree=5, rec_algorithm='SVTR_LCNet', rec_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\rec\\\\ch\\\\ch_PP-OCRv4_rec_infer', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_batch_num=6, max_text_length=25, rec_char_dict_path='D:\\\\power\\\\anaconda3\\\\envs\\\\ai\\\\Lib\\\\site-packages\\\\paddleocr\\\\ppocr\\\\utils\\\\ppocr_keys_v1.txt', use_space_char=True, vis_font_path='./doc/fonts/simfang.ttf', drop_score=0.5, e2e_algorithm='PGNet', e2e_model_dir=None, e2e_limit_side_len=768, e2e_limit_type='max', e2e_pgnet_score_thresh=0.5, e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_pgnet_valid_set='totaltext', e2e_pgnet_mode='fast', use_angle_cls=True, cls_model_dir='C:\\\\Users\\\\one/.paddleocr/whl\\\\cls\\\\ch_ppocr_mobile_v2.0_cls_infer', cls_image_shape='3, 48, 192', label_list=['0', '180'], cls_batch_num=6, cls_thresh=0.9, enable_mkldnn=False, cpu_threads=10, use_pdserving=False, warmup=False, sr_model_dir=None, sr_image_shape='3, 32, 128', sr_batch_num=1, draw_img_save_dir='./inference_results', save_crop_res=False, crop_res_save_dir='./output', use_mp=False, total_process_num=1, process_id=0, benchmark=False, save_log_path='./log_output/', show_log=True, use_onnx=False, return_word_box=False, output='./output', table_max_len=488, table_algorithm='TableAttn', table_model_dir=None, merge_no_span_structure=True, table_char_dict_path=None, formula_algorithm='LaTeXOCR', formula_model_dir=None, formula_char_dict_path=None, formula_batch_num=1, layout_model_dir=None, layout_dict_path=None, layout_score_threshold=0.5, layout_nms_threshold=0.5, kie_algorithm='LayoutXLM', ser_model_dir=None, re_model_dir=None, use_visual_backbone=True, ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ocr_order_method=None, mode='structure', image_orientation=False, layout=True, table=True, formula=False, ocr=True, recovery=False, recovery_to_markdown=False, use_pdf2docx_api=False, invert=False, binarize=False, alphacolor=(255, 255, 255), lang='ch', det=True, rec=True, type='ocr', savefile=False, ocr_version='PP-OCRv4', structure_version='PP-StructureV2', det_db_box_size=3)\n",
      "[2025/03/12 20:30:40] ppocr DEBUG: dt_boxes num : 11, elapsed : 0.5245771408081055\n",
      "[2025/03/12 20:30:40] ppocr DEBUG: cls num  : 11, elapsed : 0.11953043937683105\n",
      "[2025/03/12 20:30:41] ppocr DEBUG: rec_res num  : 11, elapsed : 0.7490806579589844\n",
      "结果图片已保存到: ./img/kongzi/output_paddleocr_global_numbered_rectangles.jpg\n",
      "矩形框信息已保存到: ./img/kongzi/rectangles_info.json\n"
     ]
    }
   ],
   "source": [
    "from paddleocr import PaddleOCR\n",
    "import cv2\n",
    "import numpy as np\n",
    "import json\n",
    "\n",
    "def draw_rectangle(image, box, color=(0, 255, 0), thickness=2):\n",
    "    \"\"\"\n",
    "    绘制轴对齐的矩形框\n",
    "    :param image: 原始图片\n",
    "    :param box: 四边形的四个顶点坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "    :param color: 框的颜色\n",
    "    :param thickness: 框的厚度\n",
    "    \"\"\"\n",
    "    # 将四边形转换为最小外接矩形\n",
    "    rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "    x, y, w, h = rect\n",
    "\n",
    "    # 绘制矩形框\n",
    "    cv2.rectangle(image, (x, y), (x + w, y + h), color, thickness)\n",
    "\n",
    "def draw_char_rectangles(image, rect, text, counter, thickness=2):\n",
    "    \"\"\"\n",
    "    绘制字符的矩形框，并交替使用红、黄、蓝三色，同时标注全局序号\n",
    "    :param image: 原始图片\n",
    "    :param rect: 文本行的矩形框 (x, y, w, h)\n",
    "    :param text: 识别文本\n",
    "    :param counter: 全局序号计数器\n",
    "    :param thickness: 框的厚度\n",
    "    :return: 更新后的全局序号计数器和矩形框信息\n",
    "    \"\"\"\n",
    "    x, y, w, h = rect\n",
    "    char_width = w / len(text)  # 假设字符宽度相等\n",
    "\n",
    "    # 定义红、黄、蓝三色\n",
    "    colors = [\n",
    "        (0, 0, 255),  # 红色\n",
    "        (0, 255, 255),  # 黄色\n",
    "        (255, 0, 0)  # 蓝色\n",
    "    ]\n",
    "\n",
    "    # 用于存储矩形框信息的字典\n",
    "    rect_info = {}\n",
    "\n",
    "    for i, char in enumerate(text):\n",
    "        # 计算当前字符的矩形框\n",
    "        char_x = int(x + i * char_width)\n",
    "        char_y = y\n",
    "        char_w = int(char_width)\n",
    "        char_h = h\n",
    "\n",
    "        # 选择颜色（红、黄、蓝交替）\n",
    "        color = colors[counter % 3]\n",
    "\n",
    "        # 绘制字符的矩形框\n",
    "        cv2.rectangle(image, (char_x, char_y), (char_x + char_w, char_y + char_h), color, thickness)\n",
    "\n",
    "        # 在矩形框内部的左上角标注全局序号\n",
    "        text_position = (char_x + 5, char_y + 20)  # 左上角偏移量\n",
    "        cv2.putText(image, str(counter), text_position, cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, thickness)\n",
    "\n",
    "        # 记录矩形框信息\n",
    "        rect_info[counter] = {\n",
    "            \"top_left\": (char_x, char_y),        # 左上角坐标\n",
    "            \"bottom_right\": (char_x + char_w, char_y + char_h)  # 右下角坐标\n",
    "        }\n",
    "\n",
    "        # 更新全局序号计数器\n",
    "        counter += 1\n",
    "\n",
    "    return counter, rect_info\n",
    "\n",
    "def main():\n",
    "    # 初始化OCR模型\n",
    "    ocr = PaddleOCR(\n",
    "        use_angle_cls=True,          # 启用方向分类\n",
    "        lang='ch',                   # 中文模型\n",
    "        det_db_thresh=0.1,           # 降低二值化阈值\n",
    "        det_db_box_thresh=0.1,       # 降低框得分阈值\n",
    "        det_db_unclip_ratio=1.5,     # 扩大框扩展比例\n",
    "        det_db_box_size=3            # 设置最小框尺寸\n",
    "    )\n",
    "\n",
    "    # 读取图片\n",
    "    image_path = './img/kongzi/a002.jpeg'\n",
    "    try:\n",
    "        image = cv2.imread(image_path)\n",
    "        if image is None:\n",
    "            raise FileNotFoundError(f\"图片 {image_path} 未找到或无法读取\")\n",
    "    except Exception as e:\n",
    "        print(f\"图片读取失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 执行OCR检测\n",
    "    try:\n",
    "        result = ocr.ocr(image_path, cls=True)\n",
    "    except Exception as e:\n",
    "        print(f\"OCR检测失败: {e}\")\n",
    "        return\n",
    "\n",
    "    # 初始化全局序号计数器\n",
    "    counter = 0\n",
    "\n",
    "    # 用于存储所有矩形框信息的字典\n",
    "    all_rect_info = {}\n",
    "\n",
    "    # 遍历所有检测到的文本行\n",
    "    for line in result:\n",
    "        for word_info in line:\n",
    "            box = word_info[0]  # 提取坐标 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]\n",
    "            text = word_info[1][0]  # 提取识别文本\n",
    "\n",
    "            # 绘制文本行的矩形框（绿色）\n",
    "            draw_rectangle(image, box, color=(0, 255, 0), thickness=2)\n",
    "\n",
    "            # 将四边形转换为最小外接矩形\n",
    "            rect = cv2.boundingRect(np.array(box, dtype=np.int32))\n",
    "\n",
    "            # 绘制字符的矩形框（红、黄、蓝交替）并标注全局序号\n",
    "            counter, rect_info = draw_char_rectangles(image, rect, text, counter, thickness=2)\n",
    "\n",
    "            # 将当前文本行的矩形框信息添加到全局字典中\n",
    "            all_rect_info.update(rect_info)\n",
    "\n",
    "    # 保存结果图片\n",
    "    output_path = './img/kongzi/output_paddleocr_global_numbered_rectangles.jpg'\n",
    "    cv2.imwrite(output_path, image)\n",
    "    print(f\"结果图片已保存到: {output_path}\")\n",
    "\n",
    "    # 保存矩形框信息为 JSON 文件\n",
    "    json_path = './img/kongzi/rectangles_info.json'\n",
    "    with open(json_path, 'w', encoding='utf-8') as f:\n",
    "        json.dump(all_rect_info, f, ensure_ascii=False, indent=4)\n",
    "    print(f\"矩形框信息已保存到: {json_path}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
